#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#define TRACE
#include <System.cu\cuPrintf.cu>
#include <System.cu\cuFalloc.cu>

#include <stdio.h>

__global__ void testFalloc(fallocHeap* heap)
{
	fallocInit(heap);

	// create/free heap
	void* obj = fallocGetChunk(heap);
	fallocFreeChunk(heap, obj);

	//void* obj2 = fallocGetChunks(heap, 144*2);
	//fallocFreeChunks(heap, obj2);

	// create/free alloc
	fallocContext* ctx = fallocCreateCtx(heap);
	char* testString = (char*)falloc(ctx, 10);
	int* testInteger = falloc<int>(ctx);
	fallocDisposeCtx(ctx);
	
	// create/free stack
	fallocContext* stack = fallocCreateCtx(heap);
	fallocPush<int>(ctx, 1);
	fallocPush<int>(ctx, 2);
	int b = fallocPop<int>(ctx);
	int a = fallocPop<int>(ctx);
	fallocDisposeCtx(ctx);
}

cudaFallocHost _fallocHost;

cudaError_t setupCuda()
{
	// Choose which GPU to run on, change this on a multi-GPU system.
    cudaError_t cudaStatus = cudaSetDevice(0);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaSetDevice failed!  Do you have a CUDA-capable GPU installed?");
        goto Error;
    }

	cudaPrintfInit(25600);
	_fallocHost = cudaFallocWTraceInit(2048);

Error:
    return cudaStatus;
}

int disposeCuda()
{
	// free and exit
	cudaFallocWTraceEnd(_fallocHost);
	cudaPrintfDisplay(stdout, true); cudaPrintfEnd();
	printf("\ndone.\n"); //scanf_s("%c");

	// cudaDeviceReset must be called before exiting in order for profiling and
	// tracing tools such as Parallel Nsight and Visual Profiler to show complete traces.
    cudaError_t cudaStatus = cudaDeviceReset();
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaDeviceReset failed!");
        return 1;
    }
	return 0;
}

int main()
{
	setupCuda();	    

	// test
	testFalloc<<<1, 1>>>(_fallocHost.heap);

	// cudaDeviceSynchronize waits for the kernel to finish, and returns
    // any errors encountered during the launch.
    cudaError_t cudaStatus = cudaDeviceSynchronize();
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
        goto Error;
    }

	// trace
	cudaFallocTrace* trace = cudaFallocTraceInit();
	void* buffer; size_t length;
	while (buffer = cudaFallocTraceStream(_fallocHost, trace, length)) {
		printf("z: %d\n", length);
	}
	cudaFallocTraceEnd(trace);

Error:
    return disposeCuda();
}
